﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Threading;

namespace BGShell.PowerShell.Controls
{
    using WinFormsTimer = System.Windows.Forms.Timer;

    partial class ConsoleControl
    {
        private enum OutputItemType
        {
            Normal,
            Debug,
            Error,
            Verbose,
            Warning,
        }

        private sealed class OutputWriter : IDisposable
        {
            private const int UpdateTimerInterval = 150;
        
            private readonly ConsoleControl _ctl;

            private OutputItemBuffer _buffer;
            private WinFormsTimer _timer;
            private Boolean _disposed;

            public OutputWriter(ConsoleControl ctl)
            {
                _ctl = ctl;
            }

            public void Dispose()
            {
                _disposed = true;

                if (_timer != null)
                {
                    _timer.Dispose();
                    _timer = null;
                }
            }

            public void AddItem(OutputItemType type, String text)
            {
                if (_disposed)
                {
                    return;
                }

                var buffNew = new OutputItemBuffer();
                var buffOld = Interlocked.CompareExchange(ref _buffer, buffNew, null);

                if (buffOld != null)
                {
                    buffNew = buffOld;
                }

                buffNew.AddItem(type, text);
            }

            public bool Enabled
            {
                get { return (!_disposed) && (_timer != null) && (_timer.Enabled); }
                set
                {
                    if ((!_disposed) && (Enabled != value))
                    {
                        if (_timer == null)
                        {
                            _timer = new WinFormsTimer();
                            _timer.Interval = UpdateTimerInterval;
                            _timer.Tick += delegate
                            {
                                _timer.Stop();
                                WriteBuffer();
                                _timer.Start();
                            };
                        }

                        _timer.Enabled = value;
                        WriteBuffer();
                    }
                }
            }

            private void WriteBuffer()
            {
                OutputItemBuffer buffer = Interlocked.Exchange(ref _buffer, null);

                if ((!_disposed) && (buffer != null))
                {
                    buffer.WriteTo(_ctl);
                }
            }
        }

        private sealed class OutputItemBuffer
        {
            private List<OutputItem> _items;

            public void AddItem(OutputItemType type, String text)
            {
                EnsureBuffer();
                _items.Add(new OutputItem(type, text));
            }

            public void WriteTo(ConsoleControl ctl)
            {
                if (_items == null || _items.Count == 0)
                {
                    return;
                }

                using (ctl.AllowProtectedUpdates())
                {
                    ConsoleControlFormatSettings colors = ctl._formatSettings;
                    OutputItemType? lastType = null;

                    foreach (var item in _items)
                    {
                        ConsoleControlFormatEntry entry = null;

                        if (lastType != item.Type)
                        {
                            lastType = item.Type;
                            entry = GetItemFormatEntry(colors, item.Type);
                        }

                        ctl.AppendTextInner(item.Text, entry);
                    }
                }

                ctl.Select(ctl.TextLength, 0);
            }

            private static ConsoleControlFormatEntry GetItemFormatEntry(ConsoleControlFormatSettings settings, OutputItemType itemType)
            {
                switch (itemType)
                {
                    case OutputItemType.Debug:
                        return settings.Debug;

                    case OutputItemType.Error:
                        return settings.Error;

                    case OutputItemType.Verbose:
                        return settings.Verbose;

                    case OutputItemType.Warning:
                        return settings.Warning;
                }

                return settings.Normal;
            }

            private void EnsureBuffer()
            {
                if (_items == null)
                {
                    _items = new List<OutputItem>();
                }
            }

            private struct OutputItem
            {
                public readonly OutputItemType Type;
                public readonly String Text;

                public OutputItem(OutputItemType type, String text)
                {
                    Type = type;
                    Text = Prefix(type, text);
                }

                private static string Prefix(OutputItemType type, String text)
                {
                    switch (type)
                    {
                        case OutputItemType.Debug:
                            return "DEBUG: " + text;

                        case OutputItemType.Verbose:
                            return "VERBOSE: " + text;

                        case OutputItemType.Warning:
                            return "WARNING: " + text;
                    }

                    return text;
                }
            }
        }
    }
}